#pragma once

#include "infra/Shelter.hpp"
#include "rewine/IImage.hpp"
#include "rewine/LibManager.hpp"
#include "rewine/PathResolver.hpp"
#include "rewine/SymbolResolver.hpp"

#include <string>

namespace rewine
{

class CCustomImage;
class CDllsoImage;
class CDsoImage;
class CPeImage;

enum class LoaderErrorCode : int {
    NONE = 0,
    FILE_NOT_FOUND = -1,
    UNKNOWN_FORMAT = -2,
};

class Loader {
private:

    LibManager      m_LibManager;
    PathResolver    m_PathResolver;
    SymbolResolver  m_SymbolResolver;

    LoaderErrorCode m_LastError;

private:

    template <class T>
    T * findExistedByFullname(std::string& fullname);

    template <class T>
    T * resolveFilenameAndFindExisted(const std::string& filename, std::string& fullname);

    CDllsoImage * loadDllsoImage(const std::string &filename);
    CDsoImage * loadDsoImage(const std::string &exportName, const std::string &filename);
    int loadPeImage(CPeImage *pImage, Shelter &shlImage);
    CPeImage * loadPeImage(const std::string& fullname, int fd);

public:

    Loader() = default;
    ~Loader() = default;

public:

    LibManager& getLibManager() {
        return m_LibManager;
    }

    PathResolver& getPathResolver() {
        return m_PathResolver;
    }

    SymbolResolver& getSymbolResolver() {
        return m_SymbolResolver;
    }

    LoaderErrorCode getLastError() {
        return m_LastError;
    }

    CCustomImage * createCustomImage(const std::string &exportName);

    IImage * loadImageBySuffix(const std::string &filename);
    IImage * loadImageByFd(int fd);

    IImage * openImage(const std::string &filename, bool bPin);
    IImage * openImage(int fd, bool bPin);
    int closeImage(IImage *pImage);

};

} // namespace rewine
